package com.gtlab1207.br_security.services;

import cn.hutool.core.util.IdUtil;
import com.googlecode.aviator.runtime.function.FunctionUtils;
import com.googlecode.aviator.runtime.type.AviatorBoolean;
import com.googlecode.aviator.runtime.type.AviatorObject;
import com.gtlab1207.br_security.common.Constant;
import com.gtlab1207.br_security.models.dao.CasbinModelDao;
import com.gtlab1207.br_security.models.domain.CasbinModel;
import com.gtlab1207.br_security.models.dto.SysUserEnforceEntity;
import lombok.extern.slf4j.Slf4j;
import org.casbin.jcasbin.main.Enforcer;
import org.casbin.jcasbin.model.Model;
import org.casbin.jcasbin.util.function.CustomFunction;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class EnforcerService {

    private final CasbinModelDao casbinModelDao;
    private final Enforcer enforcer;

    public EnforcerService(CasbinModelDao casbinModelDao, Enforcer enforcer) {
        this.casbinModelDao = casbinModelDao;
        this.enforcer = enforcer;
    }

    public Map<String,Object> selectCasbinModel() {
        Map<String,Object> map = new HashMap<>();
        List<CasbinModel> list = casbinModelDao.selectCasbinModel();
        map.put("data",list);
        map.put("total",list.size());
        return map;
    }

    @Transactional
    public Integer insertCasbinModel(String modelText) {
        CasbinModel casbinModel = new CasbinModel();
        casbinModel.setModelId(IdUtil.fastSimpleUUID());
        casbinModel.setModelDef(modelText);
        casbinModel.setModelStatus(Constant.INFORCE);
        return casbinModelDao.insertCasbinModel(casbinModel);
    }

    @Transactional
    @CacheEvict(cacheNames = {"CASBIN_MODEL_TEXT"}, key = "#casbinModel.modelId", cacheManager = "casbinRedisCacheManager")
    public Integer updateCasbinModel(CasbinModel casbinModel) {
        Integer result = 0;
        if(casbinModel.getModelId()!=null && !"".equals(casbinModel.getModelId()))
            result = casbinModelDao.updateCasbinModel(casbinModel);
        return result;
    }

    @Transactional
    @CacheEvict(cacheNames = {"CASBIN_MODEL_TEXT"}, key = "#modelId", cacheManager = "casbinRedisCacheManager")
    public Integer deleteCasbinModel(String modelId) {
        Integer result = 0;
        if(modelId!=null && !"".equals(modelId))
            result = casbinModelDao.deleteCasbinModel(modelId);
        return result;
    }

    public Map<String,Object> showPolicies() {
        Map<String,Object> map = new HashMap<>();
        List<List<String>> list = enforcer.getPolicy();
        map.put("data",list);
        map.put("total",list.size());
        return map;
    }

    public Integer removePolicy(String policy) {
        String[] strings = policy.split(",");
        if(enforcer.removePolicy(strings))
            return 1;
        return 0;
    }

    public Integer savePolicy(String policy) {
        String[] strings = policy.split(",");
        try{
            Boolean flag = enforcer.addPolicy(strings);
            if(flag)
                return 1;
        }catch (Exception e){
            e.printStackTrace();
        }
        return 0;
    }

    public Integer enforceSysUser(SysUserEnforceEntity sysUserEnforceEntity) {
//        String text = fetchModelText(sysUserEnforceEntity.getModelId());
//        Model model = buildModelFromText(text);
//        enforcer.setModel(model);
        enforcer.loadPolicy();
        CustomFunc customFunc = new CustomFunc();
        enforcer.addFunction(customFunc.getName(),customFunc);
        boolean f = enforcer.enforce(sysUserEnforceEntity.getUserRole(), sysUserEnforceEntity.getDepartment(), sysUserEnforceEntity.getProvideOrg(), sysUserEnforceEntity.getRid());
        if (f) {
            //log.info("Role {} of Department {} 's action on service Rid {} in ProvideOrg {} been denied.", sysUserEnforceEntity.getUserRole(), sysUserEnforceEntity.getDepartment(), sysUserEnforceEntity.getRid(),sysUserEnforceEntity.getProvideOrg());
            return 1;
        } else {
            log.error("Role {} of Department {} 's action on service Rid {} in ProvideOrg {} been denied.", sysUserEnforceEntity.getUserRole(), sysUserEnforceEntity.getDepartment(), sysUserEnforceEntity.getRid(),sysUserEnforceEntity.getProvideOrg());
            return 0;
        }
    }

    private Model buildModelFromText(String text) {
        Model model = new Model();
        model.loadModelFromText(text);
        return model;
    }

    @Cacheable(cacheNames = {"CASBIN_MODEL_TEXT"}, key = "#modelId", sync = true, cacheManager = "casbinRedisCacheManager")
    public String fetchModelText(String modelId) {
        return casbinModelDao.findCasbinModelById(modelId).getModelDef();
    }

    public static class CustomFunc extends CustomFunction {
        @Override
        public AviatorObject call(Map<String, Object> env, AviatorObject arg1, AviatorObject arg2) {
            String request = FunctionUtils.getStringValue(arg1, env);
            String policy = FunctionUtils.getStringValue(arg2, env);
            String []strArray = policy.split("\\|");//通过“|”分割字符串为字符串数组
            List<String> tempList = Arrays.asList(strArray);//转换为List
            boolean res = tempList.contains(request);
            return AviatorBoolean.valueOf(res);
        }
        @Override
        public String getName() {
            return "zhCn";
        }
    }
}
